package com.knight.spring.framework.web.servlet;

import com.knight.spring.framework.annotation.KnightController;
import com.knight.spring.framework.annotation.KnightRequestMapping;
import com.knight.spring.framework.context.KnightApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class KnightDispatchServlet extends HttpServlet {

    private static final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    private ServletConfig servletConfig;
    private List<KnightHandlerMapping> handlerMappings = new ArrayList<>();

    private List<KnightViewResolver> viewResolvers = new ArrayList<>();

    private Map<KnightHandlerMapping, KnightHandlerAdapter> handlerAdapters = new HashMap<>();


    @Override
    public void init(ServletConfig config) throws ServletException {
        this.servletConfig = config;
        initServletBean();
    }

    private void initServletBean() {
        // 1、通过servlet给定的初始化参数，初始化化容器
        KnightApplicationContext applicationContext =
                new KnightApplicationContext(servletConfig.getInitParameter(CONTEXT_CONFIG_LOCATION));

        //2、初始化Spring MVC的九大组件
        initStrategies(applicationContext);


    }


    protected void initStrategies(KnightApplicationContext context) {
        //初始化上传组件
        initMultipartResolver(context);
        //初始化本地化组件
        initLocaleResolver(context);
        //初始化主题组件
        initThemeResolver(context);
        //初始化handleMapping
        initHandlerMappings(context);
        //初始化参数适配组件
        initHandlerAdapters(context);
        //初始化异常处理组件
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);
        //初始化视图解析器
        initViewResolvers(context);
        //初始化参数缓存器
        initFlashMapManager(context);
    }

    private void initFlashMapManager(KnightApplicationContext context) {

    }

    private void initViewResolvers(KnightApplicationContext context) {
        //获取模板路径
        String templateRoot = context.getConfig().getProperty("templateRoot");
        //将模板路径转换成文件路径
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        for (String fileName : new File(templateRootPath).list()) {
            //这里假设配置了多个模板
            viewResolvers.add(new KnightViewResolver(templateRoot));
        }


    }

    private void initRequestToViewNameTranslator(KnightApplicationContext context) {

    }

    private void initHandlerExceptionResolvers(KnightApplicationContext context) {

    }

    private void initHandlerAdapters(KnightApplicationContext context) {
        //把一个requet请求变成一个handler，参数都是字符串的，自动配到handler中的形参

        //可想而知，他要拿到HandlerMapping才能干活
        //就意味着，有几个HandlerMapping就有几个HandlerAdapter
        for (KnightHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping, new KnightHandlerAdapter());
        }
    }

    private void initThemeResolver(KnightApplicationContext context) {

    }

    private void initHandlerMappings(KnightApplicationContext context) {
        //1、获取所有的bean name
        String[] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Object candidateBean = context.getBean(beanName);
            Class<?> candidateBeanClass = candidateBean.getClass();

            //通过bean name判断该bean是不是被KnightController注解修饰
            if (!candidateBeanClass.isAnnotationPresent(KnightController.class)) {
                continue;
            }
            String baseUrl = "";
            //如果是，则获取注解KnightRequestMapping对应的值
            if (candidateBeanClass.isAnnotationPresent(KnightRequestMapping.class)) {
                KnightRequestMapping requestMapping = candidateBeanClass.getAnnotation(KnightRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            //再获取Controller包含的所有方法，
            Method[] methods = candidateBeanClass.getMethods();

            for (Method method : methods) {
                if (!method.isAnnotationPresent(KnightRequestMapping.class)) {
                    continue;
                }
                //如果该方法添加了注解KnightRequestMapping，则获取对应的值
                KnightRequestMapping methodRequestMapping = method.getAnnotation(KnightRequestMapping.class);

                //拼装Controller和Method的url，封装到KnightHandlerMapping类中
                String regex = ("/" + baseUrl + "/" + methodRequestMapping.value().replaceAll("\\*", ".*"))
                        .replaceAll("/+", "/");

                Pattern pattern = Pattern.compile(regex);

                this.handlerMappings.add(new KnightHandlerMapping(pattern, candidateBean, method));
                System.out.println("Mapped " + regex + "," + method);
            }


        }


    }

    private void initLocaleResolver(KnightApplicationContext context) {
    }

    private void initMultipartResolver(KnightApplicationContext context) {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //通过request获取到对应的HandlerMapping
        KnightHandlerMapping handlerMapping = getHandler(req);
        if(handlerMapping == null){
            processDispatchResult(req,resp,new KnightModelAndView("404"));
            return;
        }
        //通过handlerMapping获取到对应的HandlerAdapter
        KnightHandlerAdapter ha = getHandlerAdapter(handlerMapping);

        //调用handler方法
        KnightModelAndView mv = null;
        mv = ha.handle(req, resp, handlerMapping);


        //处理生成的mv
        processDispatchResult(req, resp, mv);

    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp,
                                       KnightModelAndView mv) throws Exception {
        //把给我的ModleAndView变成一个HTML、OuputStream、json、freemark、veolcity
        //ContextType
        if (null == mv) {
            return;
        }

        //如果ModelAndView不为null，怎么办？
        if (this.viewResolvers.isEmpty()) {
            return;
        }

        for (KnightViewResolver viewResolver : this.viewResolvers) {
            KnightView view = viewResolver.resolveViewName(mv.getViewName(), null);
            view.render(mv.getModel(), req, resp);
            return;
        }
    }

    private KnightHandlerAdapter getHandlerAdapter(KnightHandlerMapping handlerMapping) {
        if (this.handlerAdapters.isEmpty()) {
            return null;
        }
        KnightHandlerAdapter ha = this.handlerAdapters.get(handlerMapping);
        if (ha.supports(handlerMapping)) {
            return ha;
        }
        return null;
    }

    private KnightHandlerMapping getHandler(HttpServletRequest req) {
        if (handlerMappings.isEmpty()) {
            return null;
        }
        String requestURI = req.getRequestURI();
        String contextPath = req.getContextPath();
        String handlerMappingURL = requestURI.replace(contextPath, "").replaceAll("/+", "/");

        for (KnightHandlerMapping handlerMapping : handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(handlerMappingURL);
            if (!matcher.matches()) {
                continue;
            }
            return handlerMapping;
        }
        return null;
    }
}
